// These three lines link in the required SDL components for our project.
// Alternatively, we could have linked them in our project settings.
#pragma comment(lib, "SDL.lib")
#pragma comment(lib, "SDLmain.lib")
#pragma comment(lib, "SDL_TTF.lib")
#pragma comment(lib, "SDL_image.lib")

#include <stack>     // We'll use the STL stack to store our function pointers
#include <vector>	 // Gotta love Vectors
#include "SDL.h"     // Main SDL header 
#include "SDL_TTF.h" // True Type Font header
#include "SDL_image.h" // Image loading header
#include "math.h"    // We'll be using abs()
#include "time.h"    // For seeding the random generator
#include "Defines.h" // Our defines header
#include "Enums.h"   // Our enums header
#include "Card.h"
#include "VanderTower.h"

using namespace std; // save us from writing "std::" everywhere

// The STL stack can't take a function pointer as a type
// so we encapsulate a function pointer within a struct.
struct StateStruct 
{
	void (*StatePointer)();
};

// Global data 
stack<StateStruct> g_StateStack;    // Our state stack
SDL_Surface* g_Bitmap = NULL;  // Our background image
SDL_Surface* g_Window = NULL;  // Our back buffer
SDL_Event g_Event;             // An SDL event structure for input
int g_Timer;                   // Our timer is just an integer

// Game-based Globals
Card* g_CardPreview;
Card* g_CardSelected;
vector<Card*> g_Cards;

// Game-related Functions

// Functions to handle the three states of the game 
void Menu();
void Game();
void Exit();

// Init and Shutdown functions
void Init();
void Shutdown();

// Helper functions for the game state functions
void ClearScreen();
void DisplayText(string text, int x, int y, int size,
            int fR, int fG, int fB, int bR, int bG, int bB);
void drawSprite(SDL_Surface* imageSurface, 
                SDL_Surface* screenSurface,
                int srcX, int srcY, 
                int dstX, int dstY,
                int width, int height);

// Input functions
void HandleMenuInput();
void HandleGameInput();
void HandleExitInput();

int main(int argc, char **argv)
{
    Init();

    // Our game loop is just a while loop that breaks when our state stack is empty.
    while (!g_StateStack.empty())
    {
        g_StateStack.top().StatePointer(); 
    }

    Shutdown();

    return 0;
}

// This function initializes our game. 
void Init()
{
    // Initiliaze SDL video and our timer. 
    SDL_Init( SDL_INIT_VIDEO | SDL_INIT_TIMER);
    // Setup our window's dimensions, bits-per-pixel (0 tells SDL to choose for us),       
    // and video format (SDL_ANYFORMAT leaves the decision to SDL). This function
    // returns a pointer to our window which we assign to g_Window. 
    g_Window = SDL_SetVideoMode(WINDOW_WIDTH, WINDOW_HEIGHT, 0, SDL_ANYFORMAT); 
    // Set the title of our window. 
    SDL_WM_SetCaption(WINDOW_CAPTION, 0);
    // Get the number of ticks since SDL was initialized. 
    g_Timer = SDL_GetTicks();

	srand( time(0) );

	g_Bitmap = IMG_Load("background.png");
	g_Cards.push_back(new Card);
	g_Cards.push_back(new VanderTower);

    // We start by adding a pointer to our exit state, this way
    // it will be the last thing the player sees of the game. 
    StateStruct state;
    state.StatePointer = Exit;
    g_StateStack.push(state);

    // Then we add a pointer to our menu state, this will 
    // be the first thing the player sees of our game. 
    state.StatePointer = Menu;
    g_StateStack.push(state);

    // Initialize the true type font library. 
    TTF_Init();
}

// This function shuts down our game. 
void Shutdown()
{
    // Shutdown the true type font library.
    TTF_Quit();

    // Free our surfaces. 
    SDL_FreeSurface(g_Bitmap);
    SDL_FreeSurface(g_Window);

    // Tell SDL to shutdown and free any resources it was using. 
    SDL_Quit();
}

// This function handles the game's main menu. From here 
// the player can select to enter the game, or quit. 
void Menu()
{
    // Here we compare the difference between the current time and the last time we 
    // handled a frame. If FRAME_RATE amount of time has, it's time for a new frame. 
    if ( (SDL_GetTicks() - g_Timer) >= FRAME_RATE )
    {
        HandleMenuInput();

        // Make sure nothing from the last frame is still drawn.
        ClearScreen();

        DisplayText("(S)tart", 440, 320, 36, 0, 255, 64, 0, 0, 0);
		DisplayText("(Q)uit", 440, 375, 36, 255, 64, 64, 0, 0, 0);

        // Tell SDL to display our backbuffer. The four 0's will make SDL display the whole screen. 
        SDL_UpdateRect(g_Window, 0, 0, 0, 0);

        // We've processed a frame so we now need to record the time at which we did it.
        // This way we can compare this time with the next time our function gets called and 
        // see if enough time has passed between calls.
        g_Timer = SDL_GetTicks();
    } 
}

// This function handles the main game. We'll control the
// drawing of the game as well as any necessary game logic. 
void Game()
{
    // Here we compare the difference between the current time and the last time we
    // handled a frame. If FRAME_RATE amount of time has, it's time for a new frame. 
    if ( (SDL_GetTicks() - g_Timer) >= FRAME_RATE )
    {
        HandleGameInput();

        // Make sure nothing from the last frame is still drawn. 
        ClearScreen();

		drawSprite(g_Bitmap, g_Window, 0, 0, 0, 0, 1024, 768);

		if(g_CardPreview)
		{
			g_CardPreview->Display(g_Window, PREVIEW_X, PREVIEW_Y, LARGE);
			DisplayText(g_CardPreview->ActionClicked, PREVIEW_X, PREVIEW_Y, 24, 255, 0, 0, 0, 0, 0);
		}

		for (int i = 0; i < g_Cards.size(); i++)
			g_Cards[i]->Display(g_Window);		

        // Tell SDL to display our backbuffer. The four 0's will make SDL display the whole screen. 
        SDL_UpdateRect(g_Window, 0, 0, 0, 0);

        // We've processed a frame so we now need to record the time at which we did it.
        // This way we can compare this time with the next time our function gets called and 
        // see if enough time has passed between calls.
        g_Timer = SDL_GetTicks();
    } 
}

// This function handles the game's exit screen. It will display 
// a message asking if the player really wants to quit.
void Exit()
{     
    // Here we compare the difference between the current time and the last time we
    // handled a frame. If FRAME_RATE amount of time has, it's time for a new frame.
    if ( (SDL_GetTicks() - g_Timer) >= FRAME_RATE )
    {
        HandleExitInput();
        // Make sure nothing from the last frame is still drawn.
        ClearScreen();

        DisplayText("Quit Game (Y or N)?", 350, 260, 12, 255, 255, 255, 0, 0, 0);

        // Tell SDL to display our backbuffer. The four 0's will make SDL display the whole screen. 
        SDL_UpdateRect(g_Window, 0, 0, 0, 0);

        // We've processed a frame so we now need to record the time at which we did it. 
        // This way we can compare this time with the next time our function gets called and 
        // see if enough time has passed between calls. 
        g_Timer = SDL_GetTicks();
    } 
}

// This function simply clears the back buffer to black. 
void ClearScreen()
{
    // This function just fills a surface with a given color. The
    // first 0 tells SDL to fill the whole surface. The second 0 
    // is for black. 
    SDL_FillRect(g_Window, 0, 0);
}

// This function displays text to the screen. It takes the text 
// to be displayed, the location to display it, the size of the 
// text, and the color of the text and background. 
void DisplayText(string text, int x, int y, int size, int fR,
                 int fG, int fB, int bR, int bG, int bB) 
{
    // Open our font and set its size to the given parameter. 
    TTF_Font* font = TTF_OpenFont("arial.ttf", size);

    SDL_Color foreground = { fR, fG, fB};   // Text color. 
    SDL_Color background = { bR, bG, bB };  // Color of what's behind the text.

    // This renders our text to a temporary surface. There 
    // are other text functions, but this one looks nice. 
    SDL_Surface* temp = TTF_RenderText_Shaded(font, text.c_str(), foreground, background);

    // A structure storing the destination of our text. 
    SDL_Rect destination = { x, y, 0, 0 };

    // Blit the text surface to our window surface, the NULL specifies the whole surface. 
    SDL_BlitSurface(temp, NULL, g_Window, &destination);

    // Always free memory! 
    SDL_FreeSurface(temp);

    // Close the font.
    TTF_CloseFont(font);
}

void drawSprite(SDL_Surface* imageSurface, 
                SDL_Surface* screenSurface,
                int srcX, int srcY, 
                int dstX, int dstY,
                int width, int height)
{
   SDL_Rect srcRect;
   srcRect.x = srcX;
   srcRect.y = srcY;
   srcRect.w = width;
   srcRect.h = height;

   SDL_Rect dstRect;
   dstRect.x = dstX;
   dstRect.y = dstY;
   dstRect.w = width;
   dstRect.h = height;

   SDL_SetColorKey(imageSurface, SDL_SRCCOLORKEY, SDL_MapRGB(screenSurface->format, 255, 0, 255) );
   SDL_BlitSurface(imageSurface, &srcRect, screenSurface, &dstRect);
}

// This function receives player input and 
// handles it for the game's menu screen. 
void HandleMenuInput() 
{
    // Fill our event structure with event information. 
    if ( SDL_PollEvent(&g_Event) )
    {
        // Handle user manually closing game window 
        if (g_Event.type == SDL_QUIT)
        { 
            // While state stack isn't empty, pop 
            while (!g_StateStack.empty())
            {
                g_StateStack.pop();
            }

            return; // game is over, exit the function
        }

        // Handle keyboard input here 
        if (g_Event.type == SDL_KEYDOWN)
        {
            if (g_Event.key.keysym.sym == SDLK_ESCAPE)
            {
                g_StateStack.pop();
                return; // this state is done, exit the function 
            }
            // Quit 
            if (g_Event.key.keysym.sym == SDLK_q)
            {
                g_StateStack.pop();
                return; // game is over, exit the function 
            }
            // Start game
            if (g_Event.key.keysym.sym == SDLK_s)
            {
                StateStruct temp;
                temp.StatePointer = Game;
                g_StateStack.push(temp);
                return; // this state is done, exit the function 
            }
        }
    }
}

// This function receives player input and //
// handles it for the main game state.     //
void HandleGameInput() 
{
	// Fill our event structure with event information. //
	if ( SDL_PollEvent(&g_Event) )
	{
		// Handle user manually closing game window //
		if (g_Event.type == SDL_QUIT)
		{			
			// While state stack isn't empty, pop //
			while (!g_StateStack.empty())
			{
				g_StateStack.pop();
			}

			return;  // game is over, exit the function
		}

		//If a mouse button was pressed
		if(g_Event.type == SDL_MOUSEBUTTONDOWN)
		{
			//If the left mouse button was pressed
			if(g_Event.button.button == SDL_BUTTON_LEFT)
			{
				for (int i = 0; i < g_Cards.size(); i++)
				{
					if (g_Cards[i]->isMouseover(g_Event.button.x, g_Event.button.y))
						g_CardSelected = g_Cards[i];

					if (g_CardPreview)
						g_CardPreview->checkMouseclick(g_Event.button.x, g_Event.button.y);
				}
			}
		}

		//If a mouse button was pressed
		if(g_Event.type == SDL_MOUSEBUTTONUP)
		{
			//If the left mouse button was pressed
			if(g_Event.button.button == SDL_BUTTON_LEFT)
			{
				g_CardSelected = NULL;
			}

			if(g_Event.button.button == SDL_BUTTON_RIGHT)
			{
				for (int i = 0; i < g_Cards.size(); i++)
				{
					if (g_Cards[i]->isMouseover(g_Event.button.x, g_Event.button.y))
						g_Cards[i]->toggleTapped();
				}				
			}
		}

		//If a mouse button was pressed
		if(g_Event.type == SDL_MOUSEMOTION)
		{
			for (int i = 0; i < g_Cards.size(); i++)
			{
				if (g_Cards[i]->isMouseover(g_Event.button.x, g_Event.button.y))
					g_CardPreview = g_Cards[i];
			}

			//If the left mouse button was pressed
			if(g_CardSelected)
				g_CardSelected->SetXY(g_Event.button.x - (g_Cards[0]->getWidth()/2), g_Event.button.y - (g_Cards[0]->getHeight()/2));
		}

		// Handle keyboard input here
		if (g_Event.type == SDL_KEYDOWN)
		{
			if (g_Event.key.keysym.sym == SDLK_ESCAPE)
			{
				g_StateStack.pop();
				
				return;  // this state is done, exit the function 
			}
		}
		if (g_Event.type == SDL_KEYUP)
		{
			// When player releases key, set any "keypressed" bool to false;
		}
	}
}

// This function receives player input and 
// handles it for the game's exit screen.
void HandleExitInput() 
{
    // Fill our event structure with event information. 
    if ( SDL_PollEvent(&g_Event) )
    {
        // Handle user manually closing game window
        if (g_Event.type == SDL_QUIT)
        { 
            // While state stack isn't empty, pop
            while (!g_StateStack.empty())
            {
                g_StateStack.pop();
            }

            return; // game is over, exit the function
        }

        // Handle keyboard input here 
        if (g_Event.type == SDL_KEYDOWN)
        {
            if (g_Event.key.keysym.sym == SDLK_ESCAPE)
            {
                g_StateStack.pop();

                return; // this state is done, exit the function 
            }
            // Yes 
            if (g_Event.key.keysym.sym == SDLK_y)
            {
                g_StateStack.pop();
                return; // game is over, exit the function 
            }
            // No 
            if (g_Event.key.keysym.sym == SDLK_n)
            {
                StateStruct temp;
                temp.StatePointer = Menu;
                g_StateStack.push(temp);
                return; // this state is done, exit the function 
            }
        }
    }
}